/**
 * GeoSOA/Spatialytics/mapstyles/contexts/EqualIntervalsThematicContext.js
 *
 * EqualIntervalsThematicContext
 *
 * Copyright (C) 2008-2009 GeoSOA Research Group, Universite Laval
 * See LICENSE file under this distribution for licensing information
 * 
 * @author Etienne Dube
 * @since 20090918
 * @author Vincent Plantegenet
 * @since 20100420
 **/

dojo.provide("GeoSOA.Spatialytics.mapstyles.contexts.EqualIntervalsThematicContext");

dojo.require("GeoSOA.Spatialytics.mapstyles.contexts.BaseThematicContext");
dojo.require("GeoSOA.Spatialytics.mapstyles.contexts.DynamicThematicContext");
dojo.require("GeoSOA.util.HTMLUtils");
dojo.require("GeoSOA.util.Observable");


/**
 * Context object for dynamic equals intervals.
 * Inherits from BaseThematicContext and DynamicThematicContext 
 * @param {Object} options Options passed to the constructor
 * 
 * @constructor
 */
dojo.declare("GeoSOA.Spatialytics.mapstyles.contexts.EqualsIntervalsThematicContext",
		[GeoSOA.Spatialytics.mapstyles.contexts.BaseThematicContext,
		 GeoSOA.Spatialytics.mapstyles.contexts.DynamicThematicContext], {
	
	constructor: function (options) {

		// current context for closures
		var that = this;
	
		var options = options || {};
		console.log(options);
		/**
		 * {Integer} options.numberOfIntervals (optional) Specifies the number
		 * of interval classes to use. Default: 5.
		 */
		this.numberOfIntervals = options.numberOfIntervals || 5;
		
		/**
		 * {Number} options.minValue (required) Lower bound (inclusive) for
		 * the first (lower) interval class.
		 */
		this.minValue = options.minValue;
	
		/**
		 * {Number} options.maxValue (required) Upper bound (exclusive) for
		 * the last (upper) interval class.
		 */
		this.maxValue = options.maxValue;
	
		/**
		 * {String} options.minColor (optional) min HTML color
		 */
		this.minColor = options.minColor || "#000000";
	
		/**
		 * {String} options.minColor (optional) min HTML color
		 */
		this.maxColor = options.maxColor || "#FFFFFF";
	
	
		this.linear = options.linear || false;
		
		this.layerObservable = new GeoSOA.util.Observable(this);
		
		this.colorGrad = null;
		this.valueGrad = null;
		this._regenColorGrad();
		
		/**
		 * Closure function to lookup the feature's value against the equal
		 * intervals and return a color according to the defined interval classes.
		 * 
		 * @param {OpenLayers.Feature} feature OpenLayers feature object to
		 * apply a style to (passed internally by OpenLayers)
		 * @return {String} HTML color code
		 * @public
		 */
		this.getColor = function (feature) {
			var val = Math.ceil(feature.attributes[that.valueAttribute]);
			var color = that.colorGrad[that._getIntervalIdx(val)];
			return color;
		};

		this.getValColor = function (val) {
			var color = that.colorGrad[that._getIntervalIdx(val)];
			return color;
		};
		
		this.getLabel = function (feature) {
			var lab = feature.attributes["memberName"];
			return lab;
		};
		
		/**
		 * Observer function (closure with the current object) to be
		 * added when calling SOLAPContext.addAfterLayerRefreshObserver().
		 * 
		 * @return {Function} callback closure function
		 * @private
		 */

		this._minmaxObserverClosure = function (solapContext) {
			// set up an observer for dynamic min/max bounds for the equals
			// interval thematic (updated to the min and max values from the vector
			// features received in each GeoJSON response)
			
			var minmax = that.calcMinMaxValues(
					solapContext.getVectors(), this.valueAttribute);
			that.minValue = minmax.min;
			
			// hack: since maxValue is an exclusive upper bound,
			// we set it to the max value from features + 1
			// should it be max + (smallest possible Number increment) instead?
			that.maxValue = minmax.max + 1;

			that._regenColorGrad();
			
			that.layerObservable.update();
		};
		this.solapContext.afterLayerRefreshObservable.register(this._minmaxObserverClosure);
		
	},

	/**
	 * Disposes of this object (unregisters itself from the solapContext)
	 */
	dispose: function () {
		this.solapContext.afterLayerRefreshObservable.unregister(this._minmaxObserverClosure);
	},
	
	/**
	 * Returns the index of the interval in which a value is present
	 * 
	 * @param {Number} value The value to compare
	 * @return {Number} Index of the interval the value is in
	 * @private
	 */
	_getIntervalIdx: function (value) {
		var theValue = value;
		
		if(this.linear==1){
			// value should be >= minValue and < maxValue
			if (value < this.minValue) {
				theValue = this.minValue;
			}
			else if (value >= this.maxValue) {
				theValue = this.maxValue;
			}
			
			var range = this.maxValue - this.minValue;
			var step = range / this.numberOfIntervals;
			
			var idx = Math.floor((theValue - this.minValue) / step);
			
			return idx;
		}
		else{
			for (var i = 0 ; i < this.valueGrad.length ; i++) {
				
				if(theValue<this.valueGrad[i]){
					return (i-1);
				}
			}		
			return (this.valueGrad.length-1);
		}
	},
	
	
	/**
	 * Generates a linear color gradient using equal intervals
	 * 
	 * @param {String} minColor HTML color for lower interval
	 * @param {String} maxColor HTML color for upper interval
	 * @param {Integer} intervals Number of intervals (including lower&upper)
	 * @return {Array} Array of HTML color codes
	 * @private
	 */
	_makeLinearColorGradient: function (minColor, maxColor, intervals) {	
	
		var minRgb = GeoSOA.util.HTMLUtils.HTMLtoRGB(minColor);
		var maxRgb = GeoSOA.util.HTMLUtils.HTMLtoRGB(maxColor);
		
		var gradient = [];
		
		for (var i = 0 ; i < intervals ; i++) {
			var pos = i/(intervals-1);
			var intervalRgb = {
					red: Math.floor(minRgb.red*(1.0-pos) + maxRgb.red * pos),
					green: Math.floor(minRgb.green*(1.0-pos) + maxRgb.green * pos),
					blue: Math.floor(minRgb.blue*(1.0-pos) + maxRgb.blue * pos)
					
			};

			gradient[i] = GeoSOA.util.HTMLUtils.RGBtoHTML(intervalRgb);
		}

		return gradient;
	},
	
	/**
	 * Generates a linear color gradient using equal intervals
	 * 
	 * @param {String} minValue Value for lower interval
	 * @param {String} maxValue Value for upper interval
	 * @param {Integer} intervals Number of intervals (including lower&upper)
	 * @return {Array} Array of interval bounds
	 * @private
	 */
	_makeLinearValueGradient: function (minValue, maxValue, intervals) {	
	
		var values = [];
		var n = 3;
		var tmp = Math.round(maxValue-minValue);
		var pas = Math.round((maxValue-minValue)/intervals);
		for (var i = 0 ; i < intervals ; i++) {
		
			var tmp_val;
			if(this.linear==1){
				tmp_val = minValue+(i*pas);
			}
			else if(this.linear==2){
				tmp_val = Math.floor(Math.round(minValue)+(Math.round((tmp)*Math.log(i))/(Math.log(intervals))));
			}
			else{
				tmp_val = Math.floor(Math.round(minValue)+(Math.round((tmp)*Math.pow(i,n))/(Math.pow(intervals,n))));
			}
			if(minValue>100){
				values[i] = Math.floor(tmp_val/100)*100;// peu efficcace
			}
			else{
				values[i]=tmp_val;
			}
			
		}

		return values;
	},
	
	/**
	 * Regenerates color gradient
	 * 
	 * @private
	 */
	_regenColorGrad: function () {
		this.colorGrad = this._makeLinearColorGradient(
					this.minColor, this.maxColor, this.numberOfIntervals);
		this.valueGrad = this._makeLinearValueGradient(this.minValue,this.maxValue,this.numberOfIntervals);

	}
	

});
